import logging

from pymysql.cursors import DictCursor

from task_service.task_context import StorerDeliverLayer
from db_pool import SF_DB
from x_pc.core.resource_config import ResourceType
from x_pc.core.system_context import SysDataCtx
from x_pc.db.command.model import CommandRoute, CommandRouteStep, WorkResource, ResourceEquRelation
from x_pc.db.command.route_service import delivery_route_choice, interior_route_choice, prework_route_choice, \
    tally_route_choice
from x_pc.db.common.service import WorkTypeService, ParameterConfigDtlService
from x_pc.db.source.model import DeliveryWorkQueue, InteriorWorkQueue, PreworkWorkQueue
from x_pc.model.model import TaskSource, WorkTypeEnum, CommandTypeCode


class WorkResourceService(object):
    """
    资源组 服务
    """

    resource_list: list[WorkResource] = []

    def __init__(self, resource_list):
        self.resource_list = resource_list

    def get_by_id(self, id: str) -> WorkResource | None:
        for r in self.resource_list:
            if r.id == id:
                return r
        return None


class ResourceEquService(object):
    """
    资源设备 服务
    """
    # 资源 设备 字典
    re_map: dict[str, set[str]] = dict()

    def __init__(self, resource_equ_list: list[ResourceEquRelation]):
        self.re_map = dict()
        self.__init_map(resource_equ_list)

    def __init_map(self, resource_equ_list: list[ResourceEquRelation]):
        for re in resource_equ_list:
            rid = re.wops_work_resourceid
            if rid not in self.re_map:
                self.re_map[rid] = set()
            self.re_map[rid].add(re.wops_equid)

    def get_elist_by_rid(self, rid) -> list[str]:
        if rid in self.re_map:
            return list(self.re_map[rid])
        return []


class CommandResourceService(object):
    """
    任务资源服务
    """
    sfdb: SF_DB = None
    sfdb_pool = None

    sys_data: SysDataCtx = None

    # 指令作业路径 列表
    route_list: list[CommandRoute] = []
    # 指令作业路径步骤 列表
    route_step_list: list[CommandRouteStep] = []

    # 资源组服务
    work_resource_service: WorkResourceService = None
    # 资源设备服务
    resource_equ_service: ResourceEquService = None

    def __init__(self, sys_data: SysDataCtx):
        self.sfdb = SF_DB()
        self.sfdb_pool = self.sfdb.get_pool()
        self.sys_data = sys_data
        self.__init_work_resource()
        self.__init_resource_equ()
        # 初始化 路由数据
        self.__init_route()
        # 根据设备初始化情况，来选择可用路径
        self.__fix_route_list_with_init_data()

    def __init_work_resource(self):
        # 资源组
        with self.sfdb_pool.get_connection() as conn:
            cursor = conn.cursor(DictCursor)
            data_list = cursor.db_query('SELECT * FROM wops_work_resource')
            resource_list = [WorkResource(**_) for _ in data_list]
        self.work_resource_service = WorkResourceService(resource_list)

    def __init_resource_equ(self):
        # 获取设备信息
        with self.sfdb_pool.get_connection() as conn:
            cursor = conn.cursor(cursor=DictCursor)
            data_list = cursor.db_query('SELECT * FROM wops_resource_equ_ralation')
            resource_equ_list = [ResourceEquRelation(**_) for _ in data_list]
        self.resource_equ_service = ResourceEquService(resource_equ_list)

    def __init_route(self):
        """
        初始化 command route
        :return:
        """
        # 设备类型
        equ_type_list = ParameterConfigDtlService.get_group_data('equ_type')
        equ_type_dict = {}
        for equ_type in equ_type_list:
            equ_type_dict[equ_type.value] = equ_type
        # 工作类型
        work_type_list = WorkTypeService.get_data()
        work_type_dict = {}
        for wt in work_type_list:
            work_type_dict[wt.work_type] = wt
        # 指令作业路径
        with self.sfdb_pool.get_connection() as conn:
            cursor = conn.cursor(cursor=DictCursor)
            data_list = cursor.db_query('SELECT * FROM wops_command_route WHERE enable = 1')
            route_list = [CommandRoute(**_) for _ in data_list]
            self.route_list = route_list
        fixed_route_list = []
        for r in self.route_list:
            if r.command_type_code == CommandTypeCode.PICK_INTEGRATE.value:
                # FIXME: 2024-9-24 临时过滤 REAL + 交付对象-楼层
                if (r.remark and (r.remark == 'REAL' or r.remark.find('-') > 0)) or (r.carry_type in ('PKD', 'PTX')):
                    fixed_route_list.append(r)
            else:
                fixed_route_list.append(r)
        self.route_list = fixed_route_list
        # 补充 路径 步骤 列表
        self.route_list = [_ for _ in self.route_list]
        # 指令作业路径步骤
        route_ids_set = set([_.id for _ in self.route_list])
        with self.sfdb_pool.get_connection() as conn:
            cursor = conn.cursor(cursor=DictCursor)
            data_list = cursor.db_query('SELECT * FROM wops_command_route_step')
            data_list = [_ for _ in data_list if _['wops_command_routeid'] in route_ids_set]
            route_step_list = [CommandRouteStep(**_) for _ in data_list]
            self.route_step_list = route_step_list
        # 补充 资源类型 及 设备ID列表
        for step in route_step_list:
            wr = self.work_resource_service.get_by_id(step.wops_work_resourceid)
            if wr:
                step.ext_resource_type = wr.resource_type
                step.ext_work_type = wr.work_type
                # 补充 工作类型
                if wr.work_type in work_type_dict:
                    step.ext_work_type_str = work_type_dict[wr.work_type].name
            equid_list = self.resource_equ_service.get_elist_by_rid(step.wops_work_resourceid)
            if not equid_list:
                warning_msg = f'[PC] 设备ID数据为空，step_id: {step.id}'
                self.sys_data.logger.warning(warning_msg)
            if equid_list:
                step.ext_equid_list = equid_list
                # 设置 equ_type
                device_resource_type = ResourceType(step.ext_resource_type) if step.ext_resource_type else None
                device_data_list = []
                device_equid_list = []
                for device_equid in step.ext_equid_list:
                    device_data = self.sys_data.get_data_by_id(device_resource_type, device_equid)
                    if not device_data:
                        device_resource_type_name = device_resource_type.name if device_resource_type else None
                        warning_msg = f'[PC] 设备数据为空(数据库), route_step: {step.id}, resource_type: {device_resource_type_name}, equid: {device_equid}'
                        self.sys_data.logger.warning(warning_msg)
                        continue
                    device_data_list.append(device_data)
                    device_equid_list.append(device_equid)
                # 设备信息不一致时
                if len(device_data_list) != len(step.ext_equid_list):
                    step.ext_remarks = f'部分设备被禁用或无数据(数据库): equid_list: {set(step.ext_equid_list).difference(set(device_equid_list))}'
                if not device_data_list:
                    step.ext_isvalid = False
                    step.ext_remarks = f'全部设备被禁用或无数据(数据库): equid_list: {set(step.ext_equid_list)}'
                step.ext_equ_type = list(set([_[SysDataCtx.DEVICE_TYPE_DB_KEY] for _ in device_data_list]))
                # 补充设备类型
                step.ext_equ_type_str = [equ_type_dict.get(str(_)).name if str(_) in equ_type_dict else None for _ in
                                         step.ext_equ_type]
        # 补充route的 step列表
        for route in self.route_list:
            route.ext_step_list = [_ for _ in route_step_list if _.wops_command_routeid == route.id]
            if not route.ext_step_list:
                warning_msg = f'[PC] 路径子步骤无数据(数据库) --> route: ({route.id}) {route.name}'
                self.sys_data.logger.warning(warning_msg)
                route.ext_isvalid = False
                route.ext_remarks = f'无子路径数据(数据库)'
            route.ext_step_list.sort(key=lambda _: _.seq)
            step_errors = []
            #
            for step in route.ext_step_list:
                if step.ext_work_type == WorkTypeEnum.ZKXSCK.value:
                    if not step.ext_equ_type or len(step.ext_equ_type) > 1:
                        self.sys_data.logger.warning('[PC] 在库路径 出口设置多个设备类型, 实际取用时仅取第一个: %s', step.ext_equ_type)
                    route.ext_zk_ports = [int(_) for _ in step.ext_equ_type] if step.ext_equ_type else None
                    break
            for step in route.ext_step_list:
                if step.ext_work_type == WorkTypeEnum.FHXSCK.value:
                    if not step.ext_equ_type or len(step.ext_equ_type) > 1:
                        self.sys_data.logger.warning('[PC] 发货路径 出口设置多个设备类型, 实际取用时仅取第一个: %s', step.ext_equ_type)
                    route.ext_ch_port = int(step.ext_equ_type[0]) if step.ext_equ_type else None
                    route.ext_ch_port_ids = step.ext_equid_list
                    # 将设备ID转换为设备code
                    ext_ch_port_codes = []
                    for equid in step.ext_equid_list:
                        unit_d = self.sys_data.unit_id_data_dict.get(f'{ResourceType.UNIT.value}_{equid}')
                        if unit_d:
                            unit_code = unit_d.get('equ_code')
                            if unit_code:
                                ext_ch_port_codes.append(unit_code)
                    route.ext_ch_port_codes = ext_ch_port_codes
                    break
            for step in route.ext_step_list:
                # 只有子步骤不可用时，才认为是错误。 # XXX: 可能丢失部分异常?
                if not step.ext_isvalid:
                    step_errors.append(step.ext_remarks)
            if step_errors:
                route.ext_isvalid = False
                route.ext_remarks = ', '.join(step_errors)
                warning_msg = f'[PC] 路径不可用(数据库) --> route: ({route.id}) {route}, reason: {route.ext_remarks}'
                self.sys_data.logger.warning(warning_msg)

    def __fix_route_list_with_init_data(self):
        """
        根据 设备状态入参 修正路由信息
        :return:
        """
        for route in self.route_list:
            if not route.ext_isvalid:
                continue
            step_errors = []
            for step in route.ext_step_list:
                device_resource_type = ResourceType(step.ext_resource_type) if step.ext_resource_type else None
                device_resource_type_str = device_resource_type.name if device_resource_type else ''
                #
                device_equid_list = []
                for device_equid in step.ext_equid_list:
                    device_data = self.sys_data.get_data_by_id(device_resource_type, device_equid)
                    device_type = device_data[SysDataCtx.DEVICE_TYPE_DB_KEY] if device_data else None
                    device_code = device_data[SysDataCtx.DEVICE_CODE_DB_KEY] if device_data else None
                    is_valid = self.sys_data.is_valid_device_raw(device_resource_type_str, device_type, device_code)
                    if not is_valid:
                        # self.sys_data.logger.warning(
                        #     f'[PC] 设备数据为空(入参), route: ({route.id}) {route.name}, step: {step.id}, resource_type: {device_resource_type_str}, equid: {device_equid}, 设备类型: {device_type}, 设备编码: {device_code}')
                        continue
                    device_equid_list.append(device_equid)
                if not device_equid_list:
                    step.ext_isvalid = False
                    step.ext_remarks = f'全部设备被禁用或无数据(入参): equ_type: ({step.ext_equ_type}){step.ext_equ_type_str} equid_list: {sorted(list(set(step.ext_equid_list)))}'
                    step_errors.append(step)
            # 校验下设备
            if step_errors:
                route.ext_isvalid = False
                route.ext_remarks = ', '.join([f'({_.id}) {_.ext_remarks}' for _ in step_errors])
                step_errors_str = '\n'.join([f'\t子步骤: ({_.id}[{_.seq}]) {_.ext_remarks}' for _ in step_errors])
                warning_msg = f'[PC] 路径不可用(入参) --> 路径route: ({route.id}) {route.name}, 原因reason: \n{step_errors_str}'
                self.sys_data.logger.warning(warning_msg)

    def get_command_route(self, rt: DeliveryWorkQueue | InteriorWorkQueue | PreworkWorkQueue,
                          nosplit_set: set | None, split_set: set | None, storer_layer: StorerDeliverLayer,
                          do_not_fix_pack_type_set: set[tuple[str, str]], all_dc_lpns_set: set[tuple[str, str]],
                          all_dc_lpns_dict: dict[tuple[str, str], list[tuple[str, str, int]]]):
        """
        获取 指令路径
        :param rt: 原始母任务数据
        :param nosplit_set: 自制(-1)/EMS 部分无需件箱换自制箱，即可直接发货
        :param split_set: 自制(-1/1) 部分需要件箱换自制箱，需要拆箱发货
        :param storer_layer: 交付对象楼层信息
        :param do_not_fix_pack_type_set: 无需 修正包规的任务
        :param all_dc_lpns_set:
        :param all_dc_lpns_dict:
        :return:
        """
        # 指令组编号
        command_group_code = rt.command_group_code
        if command_group_code == TaskSource.DELIVERY.value:
            return delivery_route_choice(rt, self.route_list, self.sys_data.logger, nosplit_set, split_set,
                                         storer_layer,
                                         do_not_fix_pack_type_set, all_dc_lpns_set, all_dc_lpns_dict)
        elif command_group_code == TaskSource.INTERIOR.value:
            return interior_route_choice(rt, self.route_list, self.sys_data.logger, all_dc_lpns_dict)
        elif command_group_code == TaskSource.PREWORK.value:
            return prework_route_choice(rt, self.route_list, self.sys_data.logger)
        elif command_group_code == TaskSource.TALLY.value:
            return tally_route_choice(rt, self.route_list, self.sys_data.logger)
        else:
            error_msg = f'[PC] 路径选择异常 未知任务类型: command_group_code->{command_group_code}'
            self.sys_data.logger.error(error_msg)
            return None

    def get_command_routes(self, command_type_code, start_pack_type, end_pack_type, is_mpq, remark: str) -> [
        CommandRoute]:
        """
        获取匹配路径
        主要用于 已计算出 路径各种筛选条件，进行快速匹配过滤
        :param command_type_code: 命令指令
        :param start_pack_type: 开始包规
        :param end_pack_type: 最终包规
        :param is_mpq: 是否需要GTP
        :param remark: REMARK
        :return:
        """
        match_routes = []
        for r in self.route_list:
            if r.command_type_code == command_type_code and r.start_pack_type == start_pack_type and r.end_pack_type == end_pack_type and r.is_mpq == is_mpq:
                if remark != r.remark:
                    continue
                match_routes.append(r)
        return match_routes


if __name__ == '__main__':
    from x_pc.db.sf_data import get_sys_init_data

    logger = logging.getLogger(__name__)

    crs = CommandResourceService(get_sys_init_data([], logger))
    print(crs.route_list)
